use crate::db::malines::KV;
use crate::db::FUDB;
///
use crate::model::kline::Kline;
use crate::model::malines::ma::MAConfig;
use crate::model::malines::ma::PeriodV;
use crate::model::malines::ma::MA;
use crate::round2;
use anyhow::anyhow;
use anyhow::ensure;
use anyhow::Result;
use surrealdb::sql::Id;
use surrealdb::sql::Thing;
use yata::core::Method;
use yata::helpers::Peekable;
use yata::methods::HMA;

#[derive(Debug)]
pub struct Hma {
    /// hma computer for each period
    hmas: Vec<(u8, HMA)>,
}

impl Hma {
    pub fn new(ma_config: &MAConfig, init_value: &f64) -> Result<Self> {
        let hmas: Vec<(u8, HMA)> = ma_config.periods
            .iter()
            .map(|&period| Self::create_hma(period, init_value))
            .collect::<Result<Vec<_>, _>>()?;
        Ok(Hma { hmas })
    }

    pub fn next(&mut self, value: &f64) -> MA<f64> {
        Self::compute(&mut self.hmas, value)
    }

    pub fn repeat(&mut self, value: &f64) -> MA<f64> {
        let mut hmas = self.hmas.clone();
        Self::compute(&mut hmas, value)
    }

    fn compute(hmas: &mut Vec<(u8, HMA)>, value: &f64) -> MA<f64> {
        for hma in hmas.iter_mut() {
            hma.1.next(value);
        }

        let mas: Vec<PeriodV<f64>> = hmas
            .iter()
            .map(|(period, hma)| PeriodV {
                period: period.clone() as u16,
                value: hma.peek(),
            })
            .collect();

        let ma = MA { values: mas };
        return ma;
    }

    // Function to create an EMA instance and handle potential errors.
    pub fn create_hma(period: u8, initial_value: &f64) -> Result<(u8, HMA)> {
        let rs = HMA::new(period, initial_value).map_err(|e|
            anyhow!("Failed to create HMA: {}", e)
        );
        match rs {
            Ok(rs) => Ok((period, rs)),
            Err(e) => Err(e),
        }
    }
}

// Function to create an HMA instance and handle potential errors.
fn create_hma(period: u8, initial_value: &f64) -> Result<(u8, HMA)> {
    let rs = HMA::new(period, initial_value).map_err(|e| anyhow!("Failed to create HMA: {}", e));
    match rs {
        Ok(rs) => Ok((period, rs)),
        Err(e) => Err(e),
    }
}

/// 更新指定K线数据的HMA,指定MA步长,现在只能做全量计算
///
/// @param klines: K线数据
///
/// config: HMA设置
///
/// return 更新的记录数
///
pub async fn update_hma(klines: &Vec<Kline>, config: &Option<MAConfig>) -> Result<u32> {
    ensure!(klines.len() > 0, "klines is empty");

    let mut updates = 0u32;

    if let Some(fstk) = klines.first() {
        let mut ma_values: Vec<KV<MA<f64>>> = Vec::new();
        let ma_config = config.as_ref().cloned().unwrap_or_default();

        let mut hmas: Vec<(u8, HMA)> = ma_config.periods
            .iter()
            .map(|&period| create_hma(period, &fstk.close))
            .collect::<Result<Vec<_>, _>>()?;

        for (i, kline) in klines.iter().enumerate() {
            if let Some(Thing { id: Id::String(kid_str), .. }) = &kline.id {
                for hma in hmas.iter_mut() {
                    hma.1.next(&kline.close);
                }

                let mas: Vec<PeriodV<f64>> = hmas
                    .iter()
                    .map(|(period, hma)| PeriodV {
                        period: period.clone() as u16,
                        value: round2(hma.peek()),
                    })
                    .collect();

                let ma = MA { values: mas };

                println!("kline[{i}] close={} ==> {:?}", kline.close, ma);

                // let updated: Option<FudaRecord> = FUDB
                //     .update(("kline", kid_str))
                //     .patch(PatchOp::replace("/hma", ma))
                //     .await
                //     .map_err(|err| anyhow!(err))?;
                ma_values.push(KV {
                    id: kline.id.clone().unwrap(),
                    value: ma,
                });

                // if let Some(_) = updated {
                updates += 1;
                // }
            }
        }

        //TODO, use surrealdb Statement For loop
        let query = FUDB.query(
            r#"FOR $kv IN $ma_values { UPDATE $kv.id SET l_hma = $kv.value ;}"#
        ).bind(("ma_values", ma_values.clone()));

        // println!(
        //     "(hma::update_hma) query={:?} \n\nma_values={:?}\n",
        //     query, ma_values
        // );
        let rs = query.await?;
        // println!("(hma::update_hma) rs= {:?}", rs);
    }

    Ok(updates)
}

#[cfg(test)]
mod test {
    use surrealdb::sql::Thing;

    use crate::db;
    use crate::db::malines::hma::Hma;
    use crate::model::kline::Kline;
    use crate::model::kline::Klt;
    use crate::model::kline::KLINE_COMMON_FIELDS;
    use crate::model::malines::ma::MAConfig;

    fn dummy_klines(size: u8) -> Vec<Kline> {
        let mut klines = Vec::new();
        for i in 0..size {
            let kl = Kline {
                id: Some(Thing::from(("kline", i.to_string().as_str()))),
                close: (i as f64) + 1.0,
                ..Default::default()
            };
            klines.push(kl);
        }
        klines
    }

    #[tokio::test]
    async fn test_hma_next() {
        let klines = dummy_klines(10);

        let mut hma = Hma::new(&MAConfig::default(), &klines[0].close).unwrap();

        for (idx, kl) in klines.iter().enumerate() {
            let value = hma.next(&kl.close);
            println!("hma[{idx:02}]: close={} => {:?}", kl.close, value);
        }
    }

    #[tokio::test]
    async fn test_hma_repeat() {
        let klines = dummy_klines(4);

        let mut hma = Hma::new(&MAConfig::default(), &klines[0].close).unwrap();

        for (idx, kl) in klines.iter().enumerate() {
            let value = hma.next(&kl.close);
            println!("hma[{idx:02}]: close={} => {:?}", kl.close, value);
        }
        println!("\n\nnhma={:?}", hma);

        let kl = Kline {
            id: Some(Thing::from(("kline", "5"))),
            close: 5.0,
            ..Default::default()
        };
        for i in 0..3 {
            let value = hma.repeat(&kl.close);
            println!("\nhma[{i}]: close={} => {:?}", kl.close, value);
        }
        println!("\n\nhma={:?}", hma);
    }

    #[tokio::test]
    async fn test_update_hma() {
        let _ = db::init().await.unwrap();
        let hma_config = Some(MAConfig::default());

        let klt_vec = vec![
            Klt::M005,
            Klt::M015,
            Klt::M030,
            Klt::M060,
            Klt::DAY,
            Klt::WEEK,
            Klt::MONTH
        ];
        for klt in &klt_vec {
            let klines = db::kline
                ::select("SZ.KZZ.128136", None, Some(klt), Some("klts ASC"), 0, 10000).await
                .unwrap();
            let result = db::malines::hma::update_hma(&klines, &hma_config).await;
            assert_eq!(result.is_ok(), true);
        }

        let result = db::kline::select(
            "SZ.KZZ.128136",
            None,
            Some(&Klt::M005),
            Some("klts ASC"),
            0,
            100
        ).await;
        if let Ok(klines) = &result {
            for (index, kline) in klines.iter().enumerate() {
                if index > 3 {
                    break;
                }
                // println!(
                //     "[{:02}], close:{:.2}=>hma:{:?}",
                //     index, kline.close, kline.malines,
                // );
            }
        }
    }
}
